// Effective C++ 条款30：透彻了解 inlining 的里里外外
// 内联函数的基本概念
// 内联函数(inline function)是C++中一种优化技术，它建议编译器将函数调用替换为函数体本身，从而消除函数调用的开销。这类似于宏展开，但内联函数提供了类型安全和其他函数特性。

// 内联的声明方式
// 有两种主要方式声明内联函数：
// 1.使用 inline 关键字：
inline int max(int a, int b)
{
    return a > b ? a : b;
}

// 2.在类定义中定义函数：
class Widget
{
public:
    int getSize() const { return size; } // 隐式内联
private:
    int size;
};

// 内联的工作原理
// 编译器如何处理内联请求
// 关键点：inline 只是对编译器的请求，不是命令。编译器可以：
// 1.接受请求并内联函数
// 2.忽略请求并正常调用函数
// 3.在某些情况下内联，其他情况不内联

// 内联的实际过程
// 当函数被内联时，编译器会：
// 1.用函数体替换函数调用
// 2.执行必要的参数替换
// 3.进行上下文相关的优化

// 例如：
inline int max(int a, int b)
{
    return a > b ? a : b;
}

void foo()
{
    int x = 5, y = 7;
    int z = max(x, y); // 内联后变为: int z = x > y ? x : y;
}

// 内联的优缺点
// 优点
// 1.消除函数调用开销：
//      无需保存/恢复寄存器
//      无需压栈/出栈参数
//      无需跳转和返回
// 2.启用上下文优化：
//      编译器可以对内联后的代码进行更多优化
//      常量折叠、死代码消除等
// 3.减少指令缓存不命中：
//      改善指令局部性
//      减少分支预测失败

// 缺点
// 1.代码膨胀：
//      每个调用点都会复制函数体
//      可能导致可执行文件变大
//      可能增加指令缓存压力
// 2.编译时间增加：
//      需要在每个调用点展开函数
//      增加编译器工作量
// 3.调试困难：
//      内联函数在调试器中可能无法单步执行
//      可能无法设置断点

// 编译器的内联决策
// 编译器通常根据以下因素决定是否内联：
// 1.函数复杂度：
//      简单函数更容易内联
//      包含循环、递归的函数通常不内联
// 2.调用频率：
//      热点函数（频繁调用）更可能被内联
//      冷点函数可能不内联以节省空间
// 3.函数特性：
//      虚函数通常不内联（运行时才能确定调用哪个版本）
//      递归函数通常有限制地内联
//      包含复杂控制流的函数可能不内联

// 内联的实际限制
// 大多数编译器会拒绝内联以下函数：
// 1.过于复杂的函数
inline void complexFunction()
{
    // 大量代码、循环、条件分支等
}

// 2.递归函数（除非递归深度可预测）：
inline int factorial(int n)
{
    return n <= 1 ? 1 : n * factorial(n - 1); // 可能不会内联
}

// 3.虚函数（除非在特定上下文中）：
class Base
{
public:
    virtual void foo() {} // 通常不会内联
};

// 4.包含复杂控制流的函数：
inline void complexControl(int x)
{
    try
    {
        if (x > 0)
            throw std::exception();
    }
    catch (...)
    {
        // 异常处理
    }
}

// 内联的最佳实践
// 何时使用内联
// 1.小型、简单函数：
inline bool isPositive(int x) { return x > 0; }

// 2.访问器和修改器
class Point
{
public:
    // 在类定义内部直接定义的成员函数会自动成为内联函数，无需显式使用inline关键字。

    int getX() const { return x; }
    void setX(int newX) { x = newX; }

private:
    int x;
};

// 模板函数（通常必须在头文件中定义）：
template <typename T>
inline T min(T a, T b) { return a < b ? a : b; }

// 何时避免内联
// 大型函数
inline void processLargeDataSet()
{
    // 大量代码...
} // 不应该内联

// 频繁修改的函数：内联函数修改会导致所有调用它的代码都需要重新编译
// 需要取地址的函数：
void (*funcPtr)() = inlineFunc; // 内联函数必须有非内联版本

// 实际案例分析
// 案例1：STL算法
// STL算法大量使用内联函数和函数对象：
template <class InputIterator, class T>
inline InputIterator find(InputIterator first, InputIterator last, const T &value)
{
    while (first != last && *first != value)
        ++first;
    return first;
}
// 这种设计允许编译器内联迭代器操作和比较操作，显著提高性能。

// 案例2：构造函数和析构函数
// 构造函数和析构函数通常是内联的好候选：
class String
{
public:
    String() : data(nullptr), length(0) {} // 简单构造函数，适合内联
    ~String() { delete[] data; }           // 简单析构函数，适合内联
private:
    char *data;
    size_t length;
};

// 案例3：性能关键代码
// 在性能关键路径上，内联可以显著提高性能：
class Vector3D
{
public:
    inline float dot(const Vector3D &other) const
    {
        return x * other.x + y * other.y + z * other.z;
    }

private:
    float x, y, z;
};

// 总结
// 内联函数是C++中重要的优化技术，但需要谨慎使用：

// 1.内联是一种权衡：在代码大小和执行速度之间取舍
// 2.编译器比人更懂内联：现代编译器有复杂的内联启发式算法
// 3.内联不是万能的：有时函数调用的开销可以忽略不计
// 4.测量很重要：不要基于假设进行优化，要基于实际测量结果
// 最后，记住Scott Meyers的建议：将内联限制在"小型、被频繁调用的函数"上，这样可以在大多数情况下获得最佳的性能/大小比。

// 运行时变量与内联函数
// 内联函数处理动态值的方式
// 当参数是运行时才能确定的值时，内联函数仍然会将函数体插入到调用点，但会保留参数的计算和传递逻辑。

// 普通函数版本
// ;
// 获取输入
// call   _ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_RSi  ; std::cin >> x
// call   _ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_RSi  ; std::cin >> y

// ; 调用普通函数
// mov    edi, [rbp-4]  ; 加载 x 到第一个参数寄存器
// mov    esi, [rbp-8]  ; 加载 y 到第二个参数寄存器
// call   normalAdd     ; 调用函数
// mov    [rbp-12], eax ; 保存结果到 sum

// 内联
// ; 获取输入
// call   _ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_RSi  ; std::cin >> x
// call   _ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_RSi  ; std::cin >> y

// ; 内联函数体
// mov    eax, [rbp-4]  ; 加载 x 到寄存器
// add    eax, [rbp-8]  ; 加上 y
// mov    [rbp-12], eax ; 保存结果到 sum

// 关键区别
// 即使参数是运行时确定的，内联函数与普通函数相比仍有这些区别：

// 没有函数调用开销：
// 内联版本没有call指令
// 不需要设置函数调用的栈帧
// 不需要保存/恢复寄存器状态
// 代码流程更直接：
// 内联版本直接在当前上下文中执行计算
// 普通函数版本需要跳转到函数代码，然后再返回
// 编译器优化机会：
// 内联版本允许编译器进行更多上下文相关优化
// 例如，如果函数结果只用于特定条件，编译器可能能够优化掉某些计算